﻿/*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Data.Common;
using System.ComponentModel;

namespace IvyOrm
{
    /// <summary>
    /// Specifies additional options to be used with the operation. 
    /// </summary>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Orm")]
    public class IvyOrmOptions
    {
        /// <summary>
        /// Will be used as the default ValueConverterFactory. When not provided will 
        /// use <see cref="IvyOrm.ValueConverterFactory.Default" />.
        /// When set to null the default will be used.
        /// </summary>
        public ValueConverterFactory ValueConverterFactory { get; set; }

        /// <summary>
        /// Additional options for the method. When not provided will 
        /// use <see cref="IvyOrm.ModificationOptions.ProviderDefault" />.
        /// When set to null the default for the provider will be used.
        /// </summary>
        public ModificationOptions? ModificationOptions { get; set; }

        /// <summary>
        /// Specifies options when performing a record query operation. 
        /// When set to null the default for the provider will be used.
        /// </summary>
        public RecordQueryOptions? RecordQueryOptions { get; set; }

        /// <summary>
        /// Overrides the default ProviderFactory used to create DbCommands and 
        /// DbParameters. 
        /// When set to null the default for the provider will be used.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")]
        public DbProviderFactory DbProviderFactory { get; set; }

        /// <summary>
        /// Provides standard output to be used for logging. 
        /// (in addition to the IvyOrm.Tracing.TraceSource)
        /// </summary>
        public Action<TraceEventType, string, Exception> LoggingFunction { get; set; }

        bool m_OutputToTraceSource = true;
        /// <summary>
        /// Enables/disables output to IvyOrm.Tracing.TraceSource
        /// </summary>
        [DefaultValue(true)]
        public bool OutputToTraceSource
        {
            get { return m_OutputToTraceSource; }
            set { m_OutputToTraceSource = value; }
        }

        /// <summary>
        /// Returns options loading using the default for the connection type where not provided.
        /// </summary>
        /// <param name="connection">Used to determine if there is a provider specific default</param>
        /// <param name="baseOptions">Used as the basis for the options, will use the options specified in the baseOptions if provided</param>
        /// <returns>A new IvyOrmOptions object with all options set</returns>
        internal static IvyOrmOptions Defaults(DbConnection connection, IvyOrmOptions baseOptions)
        {
            var connectionTypeName = connection == null ? "" : connection.GetType().Name;

            var options = (baseOptions ?? new IvyOrmOptions()).Clone();

            options.DbProviderFactory = options.DbProviderFactory ?? connection.GetFactory();
            options.ValueConverterFactory = options.ValueConverterFactory ?? ValueConverterFactory.Default;

            var modificationOptions = options.ModificationOptions ?? IvyOrm.ModificationOptions.ProviderDefault;
            if ((modificationOptions & IvyOrm.ModificationOptions.ProviderDefault) == IvyOrm.ModificationOptions.ProviderDefault)
            {
                if (connectionTypeName == KnownConnectionTypes.SQLiteConnection)
                    modificationOptions |= IvyOrm.ModificationOptions.None;
                else if (connectionTypeName == KnownConnectionTypes.MySqlConnection)
                    modificationOptions |= IvyOrm.ModificationOptions.None;
                else if (connectionTypeName == KnownConnectionTypes.FbConnection) //FireBird
                    modificationOptions |= IvyOrm.ModificationOptions.UppercaseIdentifiers;
                else if (connectionTypeName == KnownConnectionTypes.SqlConnection)
                    modificationOptions |= IvyOrm.ModificationOptions.None;
                else if (connectionTypeName == KnownConnectionTypes.SqlCeConnection)
                    modificationOptions |= IvyOrm.ModificationOptions.None;
                else if (connectionTypeName == KnownConnectionTypes.NpgsqlConnection ||
                         connectionTypeName == KnownConnectionTypes.PgSqlConnection) //PostgreSQL
                    modificationOptions |= IvyOrm.ModificationOptions.LowercaseIdentifiers;
                else if (connectionTypeName == KnownConnectionTypes.OracleConnection)
                    modificationOptions |= IvyOrm.ModificationOptions.UppercaseIdentifiers;
                else
                {
                    //Unknown provider, don't supply any defaults
                }
            }
            options.ModificationOptions = modificationOptions;
            
            options.RecordQueryOptions = options.RecordQueryOptions ?? IvyOrm.RecordQueryOptions.None;

            return options;
        }

        /// <summary>
        /// Creates a clone of current object
        /// </summary>
        /// <returns>A clone of the current object</returns>
        public IvyOrmOptions Clone()
        {
            return (IvyOrmOptions)this.MemberwiseClone();
        }
    }
}
